package com.tgr.shortLink.gateway.filter;

import com.alibaba.fastjson2.JSON;
import com.tgr.shortLink.gateway.config.Config;
import com.tgr.shortLink.gateway.dto.GatewayErrorResult;
import com.tgr.shortLink.gateway.dto.UserInfoDTO;
import com.tgr.shortLink.gateway.toolkit.JwtProperties;
import com.tgr.shortLink.gateway.toolkit.JwtUserConstant;
import com.tgr.shortLink.gateway.toolkit.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;


import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;

/**
 * 描述：
 * 作者：小陶不慌张
 * 文件：TokenValidateGatewayFilterFactory
 * 日期：2024/2/28 17:30
 */
@Component

public class TokenValidateGatewayFilterFactory extends AbstractGatewayFilterFactory<Config> {
    public TokenValidateGatewayFilterFactory(){super(Config.class);}

    public static final String DELETION_PATH="/api/short-link/admin/v1/user/logout";
    @Autowired
    private JwtProperties jwtProperties;

    @Override
    public GatewayFilter apply(Config config) {
        return ((exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String requestPath = request.getPath().toString();
            String requestMethod = request.getMethod().name();
            if(!isPathInWhiteList(requestPath,requestMethod,config.getWhitePathList())){
                String username =request.getHeaders().getFirst("username");
                String token =request.getHeaders().getFirst("token");
                UserInfoDTO userInfoDTO = JwtUtil.parseJWT("tgrShort",token);
                if(!validateToken(userInfoDTO)){
                    ServerHttpResponse response = exchange.getResponse();
                    response.setStatusCode(HttpStatus.UNAUTHORIZED);
                    return response.writeWith(Mono.fromSupplier(() -> {
                        DataBufferFactory bufferFactory = response.bufferFactory();
                        GatewayErrorResult resultMessage = GatewayErrorResult.builder()
                                .status(HttpStatus.UNAUTHORIZED.value())
                                .message("Token validation error")
                                .build();
                        return bufferFactory.wrap(JSON.toJSONString(resultMessage).getBytes());
                    }));
                }
                ServerHttpRequest.Builder builder = exchange.getRequest().mutate().headers(httpHeaders->{
                    httpHeaders.set(JwtUserConstant.USER_ID_KEY, userInfoDTO.getUserId());
                    httpHeaders.set(JwtUserConstant.USER_NAME_KEY, userInfoDTO.getUsername());
                    httpHeaders.set(JwtUserConstant.REAL_NAME_KEY, URLEncoder.encode(userInfoDTO.getRealName(), StandardCharsets.UTF_8));
                    if (Objects.equals(requestPath, DELETION_PATH)) {
                        httpHeaders.set(JwtUserConstant.USER_TOKEN_KEY, token);
                    }
                });
                return chain.filter(exchange.mutate().request(builder.build()).build());
            }
            return chain.filter(exchange);

        });
    }
    private boolean isPathInWhiteList(String requestPath, String requestMethod, List<String> whitePathList){
        //"/api/short-link/admin/v1/user"为用户注册接口
        return (!CollectionUtils.isEmpty(whitePathList)&& whitePathList.stream().anyMatch(requestPath::startsWith))||
                (Objects.equals(requestPath,"/api/short-link/admin/v1/user")&& Objects.equals(requestMethod, "POST"));
    }
    private boolean validateToken(UserInfoDTO userInfo) {
        return userInfo != null;
    }
}
